﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Thinker.SignChannel.Entities;
using Thinker.SignChannel.Excels;
using Thinker.SignChannel.Repositories;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Content;

namespace Thinker.SignChannel.People;

public class PersonAppService(
    IPersonRepository personRepository,
    LocalizedExcelAccessor excelAccessor,
    PersonManager personManager) :
    SignChannelAppService, IPersonAppService
{
    public async Task<PersonDto> CreateAsync(PersonEditDto input)
    {
        Normalize(input);
        var personInfo = ObjectMapper.Map<PersonEditDto, PersonInfo>(input);
        var person = await personManager.CreateAsync(personInfo);
        return ObjectMapper.Map<Person, PersonDto>(person);
    }

    public async Task DeleteAsync(Guid id)
    {
        await personManager.DeleteAsync(id);
    }

    public async Task<PersonClearOutput> ClearAllAsync()
    {
        var deleted = await personManager.DeleteAllAsync();
        return new PersonClearOutput { Deleted = deleted };
    }

    public async Task<IRemoteStreamContent> ExportTemplateAsync()
    {
        PersonExcelDto[] samples = [Sample()];
        return excelAccessor.CreateStream(samples, "人员信息", "人员信息-模板.xlsx");
    }

    public async Task<IRemoteStreamContent> ExportAllAsync()
    {
        var person = await personRepository.GetListAsync();
        var items = ObjectMapper.Map<List<Person>, List<PersonExcelDto>>(person);
        return excelAccessor.CreateStream(items, "人员信息", "人员信息.xlsx");
    }

    public async Task<ListResultDto<PersonDto>> ImportExcelAsync(IRemoteStreamContent file)
    {
        await using var stream = file.GetStream();
        var list = excelAccessor.ReadListFromExcel<PersonExcelDto>(stream);
        var infos = list.Select(x => ObjectMapper.Map<PersonExcelDto, PersonInfo>(x)).ToList();
        var people = await personManager.AddOrChangeAllAsync(infos);
        var items = people.Select(MapToDto).ToList();
        return new ListResultDto<PersonDto>(items);
    }

    public async Task<PersonDto> GetAsync(Guid id)
    {
        var person = await personRepository.GetAsync(id);
        return MapToDto(person);
    }

    public async Task<PagedResultDto<PersonDto>> GetListAsync(PersonListInput input)
    {
        var people = await personRepository.GetQueryableAsync();
        var filter = input.Filter;
        people = Search(filter, people);

        var total = await AsyncExecuter.CountAsync(people);

        var order = input.Sorting ?? nameof(Person.CreationTime);
        people = people.OrderBy(order);
        people = people.PageBy(input);
        var list = await AsyncExecuter.ToListAsync(people);
        var items = ObjectMapper.Map<List<Person>, List<PersonDto>>(list);
        return new PagedResultDto<PersonDto>(total, items);
    }

    public async Task<PersonDto> UpdateAsync(Guid id, PersonEditDto input)
    {
        Normalize(input);

        var personInfo = ObjectMapper.Map<PersonEditDto, PersonInfo>(input);
        var person = await personManager.UpdateAsync(id, personInfo);
        return MapToDto(person);
    }


    private static IQueryable<Person> Search(string? filter, IQueryable<Person> people)
    {
        if (string.IsNullOrWhiteSpace(filter))
        {
            return people;
        }

        return people.Where(x =>
            x.PersonName.Contains(filter) ||
            x.IdNumber.Contains(filter) ||
            x.Sex!.Contains(filter) ||
            x.PersonCode!.Contains(filter) ||
            x.PersonType!.Contains(filter)
        );
    }

    private PersonDto MapToDto(Person person)
    {
        return ObjectMapper.Map<Person, PersonDto>(person);
    }


    private static PersonExcelDto Sample()
    {
        return new PersonExcelDto
        {
            PersonName = "张三",
            IdNumber = "100000000000000001",
            Sex = "男",
            PersonType = "组长",
            PersonCode = "12345"
        };
    }

    private static void Normalize(PersonEditDto dto)
    {
        dto.IdNumber = dto.IdNumber?.Replace(" ", "").ToUpperInvariant();
        dto.PersonName = dto.PersonName?.Replace(" ", "");
        dto.Sex = dto.Sex?.Replace(" ", "");
        dto.PersonType = dto.PersonType?.Replace(" ", "").ToUpperInvariant();
        dto.PersonCode = dto.PersonCode?.Replace(" ", "").ToUpperInvariant();
    }
}